﻿//-----------------------------------------------------------------------
// <copyright file="IMergeCellInternal.cs" company="LMT">
//     Copyright (c) by La Minh Thuận.
// </copyright>
// <author>thuanla1985@hotmail.com</author>
//-----------------------------------------------------------------------
namespace LMT.Forms
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Windows.Forms;
    
    /// <summary>
    /// A specify Column class has a merge ability
    /// </summary>
    [ToolboxBitmap(typeof(DataGridViewTextBoxColumn))]
    public abstract class MergeGridViewColumn : DataGridViewColumn
    {
        /// <summary>
        /// Custom CellHeader for this column type.
        /// </summary>
        private MergeGridViewCellHeader mergerHr;

        /// <summary>
        /// Variable detect wherether this column is merged.
        /// </summary>
        private MergeDirection mergeDir;

        /// <summary>
        /// Determines Will all merge cells edit at the same time.
        /// </summary>
        private bool isMergeEditAll;

        /// <summary>
        /// determine wheter this column is the first spanning of merge area
        /// </summary>
        private bool isFirstSpanning;

        /// <summary>
        /// value for header spanning.
        /// </summary>
        private string hedSpanText;

        /// <summary>
        /// Initializes a new instance of the <see cref="MergeGridViewColumn"/> class.
        /// </summary>
        /// <param name="viewCell">The view cell.</param>
        internal MergeGridViewColumn(DataGridViewCell viewCell) : base(viewCell)
        {
            this.InitColumn();
        }

        /// <summary>
        /// Gets or sets the merge direction.
        /// </summary>
        /// <value>The merge direction.</value>
        [Category("Merging"), Description("Determines the merge direction.")]
        public MergeDirection MergeDirection 
        {
            get 
            { 
                return this.mergeDir; 
            }

            set
            {
                this.mergeDir = value;
                this.IsMergeCellEditAll = value != MergeDirection.None;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is merge cell edit all.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is merge cell edit all; otherwise, <c>false</c>.
        /// </value>
        [Category("Merging"), Description("Determines Will all merge cells edit at the same time.")]
        public bool IsMergeCellEditAll 
        {
            get 
            { 
                return this.isMergeEditAll; 
            }

            set
            {
                this.isMergeEditAll = value;
                if (value && this.mergeDir == MergeDirection.None)
                {
                    this.isMergeEditAll = false;
                }
            } 
        }

        /// <summary>
        /// Gets or sets the vertical merge by.
        /// </summary>
        /// <value>The vertical merge by.</value>
        [Category("Merging"), Description("Determines the rule of merging.")]
        public MergeRule MergeRule { get; set; }

        /// <summary>
        /// Gets or sets the header spaning.
        /// </summary>
        /// <value>The header span.</value>
        [Category("Merging"), Description("Determines how many row will spanning from this column.")]
        public string HeaderSpan
        {
            get
            {
                return this.hedSpanText;
            }

            set
            {
                this.ParseHeaderSpan(value);
            }
        }

        /// <summary>
        /// Gets or sets the header row split.
        /// </summary>
        /// <value>The header row split.</value>
        [Category("Merging"), Description("Determines how many row will spliting in header column.")]
        public short HeaderRow
        {
            get 
            { 
                return this.mergerHr.SplitRow;
            }

            set 
            {
                this.ParseHeaderRow(value);
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is first spaning.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is first spaning; otherwise, <c>false</c>.
        /// </value>
        public bool IsFirstSpanning
        {
            get { return this.isFirstSpanning; }
        }

        /// <summary>
        /// Return a MergeGridViewTextColumn object when editing on DataGridProperty
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Object"/> that represents the cloned <see cref="T:System.Windows.Forms.DataGridViewBand"/>.
        /// </returns>
        /// <PermissionSet>
        ///   <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        ///   <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        ///   <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public override object Clone()
        {
            MergeGridViewColumn col = base.Clone() as MergeGridViewColumn;
            col.mergerHr = this.mergerHr;
            col.MergeRule = this.MergeRule;
            col.IsMergeCellEditAll = this.IsMergeCellEditAll;
            col.MergeDirection = this.MergeDirection;
            col.mergeDir = this.mergeDir;
            col.hedSpanText = this.hedSpanText;

            return col;
        }

        /// <summary>
        /// Inits the column.
        /// </summary>
        internal void InitColumn()
        {
            if (this.HeaderCell != null)
            {
                this.HeaderCell.Dispose();
            }

            this.mergerHr = new MergeGridViewCellHeader();
            this.HeaderCell = this.mergerHr;
            this.MergeDirection = MergeDirection.None;
            this.IsMergeCellEditAll = false;
            this.MergeRule = MergeRule.Content;
            this.MergeRule = MergeRule.Content;
            this.hedSpanText = "1";
        }

        /// <summary>
        /// Applies the merging layout.
        /// </summary>
        internal void ApplyMergingLayout()
        {
            string[] spaning = this.hedSpanText.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            MergeGridViewCellHeader merCol = null;
            for (int rowIndex = 0; rowIndex < this.mergerHr.LeftOffSet.Length; rowIndex++)
            {
                if (int.Parse(spaning[rowIndex]) < 2)
                {
                    continue;
                }

                for (int index = this.DisplayIndex, column = 0; column < int.Parse(spaning[rowIndex]); column++, index++)
                {
                    merCol = this.DataGridView.Columns[index].HeaderCell as MergeGridViewCellHeader;
                    merCol.IsSpanning[rowIndex] = true;
                    merCol.LeftOffSet[rowIndex] = (ushort)this.DisplayIndex;
                    merCol.RightOffSet[rowIndex] = (ushort)(this.DisplayIndex + int.Parse(spaning[rowIndex]) - 1);
                    merCol.IsInitDraw = false;
                }
            }
        }

        /// <summary>
        /// Parses the header span.
        /// </summary>
        /// <param name="spanT">The span T.</param>
        private void ParseHeaderSpan(string spanT)
        {
            string[] spaning = spanT.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            if (spaning.Length != this.mergerHr.SplitRow)
            {
                throw new ArgumentException("Too many parameter, please verify the number of row.");
            }

            if (this.DataGridView != null)
            {
                short parseInt = 0;
                for (short index = 0; index < spaning.Length; index++)
                {
                    if (!short.TryParse(spaning[index], out parseInt))
                    {
                        throw new ArgumentException("Integer required, the value [" + spaning[index] + "] is not an integer. ");
                    }

                    if (this.DataGridView.Columns.Count - this.DisplayIndex < parseInt)
                    {
                        throw new ArgumentException("Not enough MergeGridViewColumn for spanning.");
                    }

                    if (parseInt > 1)
                    {
                        this.isFirstSpanning = true;
                    }
                }
            }

            this.hedSpanText = spanT;
        }

        /// <summary>
        /// Parses the header row.
        /// </summary>
        /// <param name="value">The value.</param>
        private void ParseHeaderRow(short value)
        {
            if (value < 1)
            {
                throw new ArgumentException("Value must be from 1 to 5.");
            }

            this.mergerHr.SplitRow = value;

            this.hedSpanText = string.Empty;
            for (ushort index = 0; index < value; index++)
            {
                this.hedSpanText += "1, ";
            }

            this.hedSpanText = this.hedSpanText.Remove(this.hedSpanText.Length - 2, 2);

            if (this.DataGridView != null)
            {
                this.DataGridView.Invalidate();
            }
        }
    }
}